package czk.concrent.stampLock.stampLockPractice;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

/**
 * @Author:ChenZhangKun
 * @Date: 2021/6/2 9:47
 */
public class StampLockPractice {
    public static void main(String[] args) throws InterruptedException {
        Book book = new Book();
        // 新开读锁线程
        new Thread(() -> {
            book.read();
        }).start();
        TimeUnit.SECONDS.sleep(3);
        // 新开线程写
        new Thread(() -> {
            book.write();
        }).start();
    }
}

/**
 * reentrantLocke的痛点
 * 1 饥饿问题：ReentrantReadWriteLock实现了读写分离，想要获取读锁就必须确保当前没有其他任何读写锁了，
 * 但是一旦读操作比较多的时候，想要获取写锁就变得比较困难了，因为当前有可能会一直存在读锁。而无法获得写锁。
 */
class Book {
    /**
     * 定义数据
     */
    private int data = 1;
    /**
     * 定义锁
     */
    private final StampedLock lock = new StampedLock();

    /**
     * 读锁
     */
    public void read() {
        // 读数据
        // 尝试使用乐观锁
        // 提供了tryOptimisticRead方法返回一个非0的stamp，只有当前同步状态没有被写模式所占有是才能获取到。
        // 乐观读取模式仅用于短时间读取操作时经常能够降低竞争和提高吞吐量。
        long optimisticLock = lock.tryOptimisticRead();
        // 邮戳
        System.out.println("乐观锁的邮戳为" + optimisticLock);
        //校验乐观锁
        if (lock.validate(optimisticLock)) {
            System.out.println("乐观锁没被修改");
            // 拿到悲观读锁
            optimisticLock = lock.readLock();
            System.out.println(data);
        }
        // 修改乐观锁
        System.out.println("乐观锁被修改了");
        // 升级为写锁
        try {
            // optimisticLock = lock.readLock();
            System.out.println("升级后的邮戳为" + optimisticLock);
            System.out.println(data);
        } finally {
            // 释放锁
            lock.unlock(optimisticLock);
            System.out.println("释放锁成功");
        }
    }

    public void write() {
        // 拿到写锁
        long writeLock = this.lock.writeLock();
        // 写锁时间戳
        System.out.println(writeLock);
        try {
            data = 2;
            System.out.println(data);
        } finally {
            lock.unlock(writeLock);
            System.out.println("释放写锁了");
        }
    }
}
